package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(TPMonitorFilter.class);

    private static Map<String, ArrayList<Monitor>> timeMap = new HashMap<>();

    private static Integer count  = 0;

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if(count == 0){
            synchronized (TPMonitorFilter.class){
                if(count == 0){
                    count++;
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            count++;
                            printTP90Monitor();
                        }
                    }).start();
                }
            }
        }

        long   startTime  = System.currentTimeMillis();
        try {
            // 执行方法
            return  invoker.invoke(invocation);
        } finally {
            boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
            //判断是否是异步调用
            if(!async){
                long handleTime = System.currentTimeMillis()-startTime;
                String key = invoker.getInterface().getSimpleName()
                        + "=>" + invocation.getMethodName();
                ArrayList<Monitor> arrayList = timeMap.get(key);
                if(CollectionUtils.isEmpty(arrayList)){
                    arrayList = new ArrayList();
                }
                Monitor monitor = new Monitor();
                monitor.setSpanTime(handleTime);
                monitor.setExpireTime(System.currentTimeMillis());
                arrayList.add(monitor);
                timeMap.put(key,arrayList);
            }
        }
    }

    /**
     * 每5秒钟，打印最近一分钟T90请求耗时
     */
    public static void printTP90Monitor(){
        while (true){
            log.info("#######################T90打印开始#############################");
            for(Map.Entry<String, ArrayList<Monitor>> entry : timeMap.entrySet()){
                String key = entry.getKey();
                ArrayList<Monitor> list = entry.getValue();
                Object[] objects = list.toArray();
                Arrays.sort(objects,Collections.reverseOrder());
                int per = (int)(Math.ceil(objects.length * 0.9));
                System.out.println("方法：" + key + " , TP90最近一分钟被调用情况，耗时如下 >>>>>>> ：");
                for (int i = 0; i < per; i++) {
                    Object object = objects[i];
                    Monitor monitor = (Monitor)object;
                    if(System.currentTimeMillis() - monitor.getExpireTime() <= 60*1000){
                        System.out.print(monitor.getSpanTime() + "ms,");
                    }
                }
                System.out.println(" ");
            }
            log.info("#######################T90打印结束#############################" + "\n");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            log.info("####################### T99打印开始 #######################");
            printTP99Monitor();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("####################### T99打印结束 #######################" + "\n");
        }
    }

    /**
     * 每5秒钟，打印最近一分钟T99请求耗时
     */
    public static void printTP99Monitor(){

            for(Map.Entry<String, ArrayList<Monitor>> entry : timeMap.entrySet()){
                String key = entry.getKey();
                ArrayList<Monitor> list = entry.getValue();
                Object[] objects = list.toArray();
                Arrays.sort(objects,Collections.reverseOrder());
                int per = (int)(Math.ceil(objects.length * 0.99));
                System.out.println("方法：" + key + " , TP99最近一分钟被调用情况，耗时如下 >>>>>>> ：");
                for (int i = 0; i < per; i++) {
                    Object object = objects[i];
                    Monitor monitor = (Monitor)object;
                    if(System.currentTimeMillis() - monitor.getExpireTime() <= 60*1000){
                        System.out.print(monitor.getSpanTime() + "ms,");
                    }
                }
                System.out.println(" ");
            }
    }

    private class Monitor implements Comparable<Monitor>{

        private long spanTime;
        private long expireTime;

        public long getSpanTime() {
            return spanTime;
        }

        public void setSpanTime(long spanTime) {
            this.spanTime = spanTime;
        }

        public long getExpireTime() {
            return expireTime;
        }

        public void setExpireTime(long expireTime) {
            this.expireTime = expireTime;
        }

        @Override
        public String toString() {
            return "Monitor{" +
                    "spanTime=" + spanTime +
                    ", expireTime=" + expireTime +
                    '}';
        }

        @Override
        public int compareTo(Monitor monitor) {
            return monitor.getSpanTime() > spanTime ? -1 : 0;
        }
    }

}